#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <cerrno>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <functional>
#include "Thread.hpp"
#include "RingQueue.hpp"
#include <unordered_map>
#include "MutexGuard.hpp"
using namespace std;

enum
{
    USAGE_ERR,  // 运行服务器时输入错误
    SOCKET_ERR, // 创建套接字失败
    BIND_ERR,   // 绑定失败
};

using func_t = function<string(const string &)>; // 和typedef一样的功能

// 127.0.0.1: 本地环回，就表示的就是当前主机，通常用来进行本地通信或者测试
const static int static_port = 8080;

class Server
{
public:
    // 构造初始化
    Server(int port = static_port)
        : _port(port)
    {
        // 将锁也进行初始化
        pthread_mutex_init(&_mutex, nullptr);
        // 线程初始化
        _r = new Thread(1, bind(&Server::Receive, this));
        _s = new Thread(2, bind(&Server::BroadCast, this));
    }

    void Start()
    {

        // 1.创建socket接口，打开网络文件
        // AF_INET表示是IPv4，sock_DGRAM表示无连接服务，UDP使用
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socket < 0)
        {
            cout << strerror(errno) << endl;
            exit(SOCKET_ERR);
        }
        cout << "create socket success:" << _socket << endl;

        // 2.给服务器指定IP地址和port
        struct sockaddr_in local; // 这个local在栈帧上，不在内核中，没有和内核建立联系
        bzero(&local, sizeof(local));
        local.sin_port = htons(_port); // 端口
        // 云服务器，或者一款服务器，一般不要指明某一个确定的ip,需要让服务器自己指定IP地址，自己本地安装的虚拟机或者物理机器允许
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        if (bind(_socket, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            cout << strerror(errno) << endl;
            exit(BIND_ERR);
        }
        cout << "bind socket success" << endl;
        // cout<<local.sin_addr.s_addr<<":"<<local.sin_port<<endl;

        // 3.接受和发送线程开始执行
        _r->Run();
        _s->Run();
    }

    // 添加用户
    void AddUser(string str, struct sockaddr_in addr)
    {
        // 保证线程安全，AddUser是接收一部分，发送部分需要进行循环遍历，因此会有线程安全问题
        MutexGuard m(&_mutex);
        auto it = _onlineuser.find(str);
        // ！end()表示找到了str，不需要再添加了
        if (it != _onlineuser.end())
        {
            return;
        }
        else
        {
            // 进行添加用户
            _onlineuser.insert(make_pair(str, addr));
        }
    }

    // 接收请求函数
    void Receive()
    {
        while (1)
        {
            char buff[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 接收从客户端发来的信息
            int n = recvfrom(_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                buff[n] = '\0';
            }
            else
            {
                cout << "Dont't receive any message" << endl;
                continue;
            }

            string ip = inet_ntoa(peer.sin_addr);
            int port = ntohs(peer.sin_port);
            // 打印出每一个客户端发来的消息-debug
            cout << ip << ":" << port << "->" << buff << endl;

            // 对遇到的每一个用户进行添加
            string name(ip);
            name += ':';
            name += to_string(port);
            // 增加用户
            AddUser(name, peer);

            // 将消息放入环形队列中，即可变成生产者消费者模型，保证了收发的正确运行
            // 只是单纯的存储在全局缓冲区中会造成覆盖
            string message(name+">>"+buff);
            _rq.push(message);
        }
    }
    // 广播，将信息发送给每一个添加进onlineuser的客户端
    void BroadCast()
    {

        while (1)
        {
            string message;
            _rq.pop(&message);
            // 将用户（IP+port）全部添加进obj，方便后续的sendto
            vector<struct sockaddr_in> obj;

            // 保证加锁粒度细一点
            {
                // 保证线程安全，AddUser是接收一部分，可能需要添加，这部分需要进行循环遍历，因此会有线程安全问题
                MutexGuard m(&_mutex);

                for (auto &e : _onlineuser)
                {
                    obj.push_back(e.second);
                }
            }

            // 给每一个用户把聊天信息传过去
            for (auto &client : obj)
            {
                sendto(_socket, message.c_str(), message.size(), 0, (struct sockaddr *)&client, sizeof(client));
            }
        }
    }
    ~Server()
    {
        _r->join();
        _s->join();
        delete _r;
        delete _s;
        pthread_mutex_destroy(&_mutex);
    }

private:
    int _socket; // 套接字描述符
    int _port;   // 端口
    // func_t _func;                                          // 处理业务接口
    Thread *_r;                                            // 接受请求线程
    Thread *_s;                                            // 响应请求的线程
    unordered_map<string, struct sockaddr_in> _onlineuser; // 加入群聊的用户
    RingQueue<string> _rq;                                 // 环形队列，将消息全部放入其中
    pthread_mutex_t _mutex;                                // 保证_onlineuser的安全
};